/* Foley C# Utilities
 * Copyright (C)2002 Rodney S. Foley
 *  
 * This program is free software; you can 
 * redistribute it and/or modify it under the 
 * terms of the GNU General Public License as 
 * published by the Free Software Foundation; 
 * either version 2 of the License, or (at your 
 * option) any later version.
 * 
 * This program is distributed in the hope that 
 * it will be useful, but WITHOUT ANY WARRANTY; 
 * without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR 
 * PURPOSE. See the GNU General Public License 
 * for more details.
 * 
 * You should have received a copy of the GNU 
 * General Public License along with this 
 * program; if not, write to: 
 * 
 *		Free Software Foundation, Inc., 
 *		59 Temple Place, Suite 330, 
 *		Boston, MA 02111-1307 USA
 * 
 *					- or -
 * 
 * http://opensource.org/licenses/gpl-license.php
 */

using System;
using System.Collections;

namespace Foley.Utilities.Collections
{
	/// <summary>
	/// Provides an abstract implementation of the <see cref="ISet"/> interface 
	/// and extends <see cref="Collection2Base"/>.
	/// </summary>	
	/// <remarks>
	/// The main purpose of this is an all purpose base class that can be 
	/// extended by any class that wants to be a set.
	/// </remarks>
	public abstract class SetBase : Collection2Base, ISet
	{
		#region Protected Constructor
		/// <summary>
		/// Protected constructor for abstract class.
		/// </summary>
		protected SetBase() {}
		#endregion

		#region Public Member Methods
		/// <summary>
		/// When implemented by a class, adds an <see cref="object"/> to the set.
		/// </summary>
		/// <param name="value">
		/// <c>true</c> if the set is modified; otherwise <c>false</c>.
		/// </param>
		/// <returns>
		/// The zero-based index at which the <see cref="object"/> was added.
		/// </returns>
		public abstract bool Add(object value);

		/// <summary>
		/// Adds the elements of the specified <see cref="ICollection"/> to the 
		/// set.
		/// </summary>
		/// <param name="collection">
		/// The <see cref="ICollection"/> whose elements are added to the set.
		/// </param>
		/// <returns>
		/// <c>true</c> if the set is modified; otherwise <c>false</c>.
		/// </returns>
		/// <exception cref="NullReferenceException">
		/// The <paramref name="collection"/> is a <c>null</c> reference 
		/// (<c>Nothing</c> in Visual Basic).
		/// </exception>
		public virtual bool Add(ICollection collection)
		{
			bool beenModified = false;

			foreach (object value in collection)
				beenModified |= Add(value);

			return beenModified;
		}

		/// <summary>
		/// Determines whether the specified <see cref="object"/> is in the set.
		/// </summary>
		/// <param name="value">
		/// The <see cref="object"/> to check if it is contained in the set.
		/// </param>
		/// <returns>
		/// <c><c>true</c></c> if the <see cref="object"/> is found in the set; 
		/// otherwise, <c>false</c>.
		/// </returns>
		/// <remarks>
		/// This method determines equality by calling <see cref="object.Equals"/>.
		/// Keep in mind that the <see cref="object"/> to locate can be a 
		/// <c>null</c> reference (<c>Nothing</c> in Visual Basic).  If you do 
		/// not want this behaviour you must override this method.
		/// </remarks>
		public virtual bool Contains(object value)
		{
			bool contains = false;

			if (value == null)
			{
				foreach (object obj in this)
					if (obj == null)
					{
						contains = true;
						break;
					}
			}
			else
			{
				foreach (object obj in this)
					if (obj.Equals(value))
					{
						contains = true;
						break;
					}
			}

			return contains;
		}

		/// <summary>
		/// Determines whether all the elements in the specified <see cref="ICollection"/> 
		/// are contained in the set.
		/// </summary>
		/// <param name="collection">
		/// The <see cref="ICollection"/> whose elements are to be checked if 
		/// all of its elements are contained in the set.
		/// </param>
		/// <returns>
		/// <c>true</c> if all the elements of the <see cref="ICollection"/> are
		/// found in the set; otherwise, <c>false</c>.
		/// </returns>
		/// <exception cref="NullReferenceException">
		/// The <paramref name="collection"/> is a <c>null</c> reference 
		/// (<c>Nothing</c> in Visual Basic).
		/// </exception>
		/// <remarks>
		/// This method determines equality by calling <see cref="object.Equals"/>.
		/// Keep in mind that the <see cref="object"/> to locate can be a 
		/// <c>null</c> reference (<c>Nothing</c> in Visual Basic).  If you do 
		/// not want this behaviour you must override this method.
		/// </remarks>
		public virtual bool Contains(ICollection collection)
		{
			bool containsAll = true;

			// can throw NullReferenceException if collection is null
			foreach (object value in this)
			{
				if (!Contains(value))
				{
					containsAll = false;
					break;
				}
			}

			return containsAll;
		}
		
		/// <summary>
		/// When implemented by a class, removes the first occurrence of a 
		/// specific <see cref="object"/> from the set.
		/// </summary>
		/// <param name="value">
		/// The <see cref="object"/> to remove from the set.
		/// </param>
		/// <returns>
		/// <c>true</c> if the set is modified; otherwise <c>false</c>.
		/// </returns>
		public abstract bool Remove(object value);
		
		/// <summary>
		/// Removes the first occurrence of each element of the specified 
		/// <see cref="ICollection"/> from the set.
		/// </summary>
		/// <param name="collection">
		/// The <see cref="ICollection"/> used to determine what 
		/// elements to remove from the set.
		/// </param>
		/// <returns>
		/// <c>true</c> if the set is modified; otherwise <c>false</c>.
		/// </returns>
		/// <exception cref="NotSupportedException">
		/// <para>The set is read-only.</para>
		/// -or-
		/// <para>The set has a fixed size.</para>
		/// </exception>
		public virtual bool Remove(ICollection collection)
		{
			bool modified = false;

			foreach (object value in collection)
				modified |= Remove(value);

			return modified;
		}

		/// <summary>
		/// Retains the first
		/// occurrence of each element of the specified
		/// <see cref="ICollection"/> from the set.
		/// </summary>
		/// <param name="collection">
		/// The <see cref="ICollection"/> used to determine what 
		/// elements to be retained in the set.
		/// </param>
		/// <returns>
		/// <c>true</c> if the set is modified; otherwise <c>false</c>.
		/// </returns>
		/// <exception cref="NotSupportedException">
		/// <para>The set is read-only.</para>
		/// -or-
		/// <para>The set has a fixed size.</para>
		/// </exception>
		public virtual bool Retain(ICollection collection)
		{	
			bool modified = false;

			LinkedSet objectsToRemove = new LinkedSet();

			foreach (object value in this)
			{
				if (!IsContainedIn(collection, value))
					objectsToRemove.Add(value);					
			}

			foreach (object value in objectsToRemove)
				Remove(value);				
			
			if (0 < objectsToRemove.Count)
				modified = true;

			return modified;
		}	
		#endregion
	}
}
